package com.yuyou.fn.tas.pusher.service;


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.github.wens.mybatisplus.toolkit.IdWorker;
import com.google.common.base.*;
import com.google.common.base.Optional;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimaps;
import com.yuyou.fn.common.exception.BusException;
import com.yuyou.fn.common.util.*;
import com.yuyou.fn.educational.entity.*;
import com.yuyou.fn.educational.service.*;
import com.yuyou.fn.educational.vo.StudentClassVo;
import okhttp3.*;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import vo.TasRegVo;
import vo.TasStudentVo;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
public class TasDataPushServiceImpl implements IDataPushService {

    private static final Logger log = LoggerFactory.getLogger(TasDataPushServiceImpl.class);

    private static final OkHttpClient httpClient = OkHttpClientUtils.getIgnoreSsLClient(60,60);

    private static final Pattern yearPattern = Pattern.compile("(20\\d{2})");

    private static final Map<Long,String>  sysCode2AreaTeamId = Maps.newHashMap();
    private static final Map<Long,String>  gradeMap = Maps.newHashMap();
    private static final Map<Long,String>  subjectMap = Maps.newHashMap();
    private static final Map<Integer,String>  levelMap = Maps.newHashMap();
    private static final Map<Integer,String>  teacherAccessTypeMap = Maps.newHashMap();
    private static final Map<Integer,String>  teacherJobStatusTypeMap = Maps.newHashMap();


    static {
        //0002广州，0003惠州，0004南宁
        sysCode2AreaTeamId.put(564701030634225664l,"0002");
        sysCode2AreaTeamId.put(565541463497113600l,"0004");
        sysCode2AreaTeamId.put(599411894062678016l,"0003");
        sysCode2AreaTeamId.put(607495955062718464l,"0003");
        sysCode2AreaTeamId.put(1027114332118241280l,"0008");
        sysCode2AreaTeamId.put(1714461766180220928l,"0009");

        gradeMap.put(11l,"1");
        gradeMap.put(12l,"2");
        gradeMap.put(13l,"3");
        gradeMap.put(14l,"4");
        gradeMap.put(15l,"5");
        gradeMap.put(16l,"6");
        gradeMap.put(21l,"7");
        gradeMap.put(22l,"8");
        gradeMap.put(23l,"9");
        gradeMap.put(31l,"11");
        gradeMap.put(32l,"12");
        gradeMap.put(33l,"13");
        gradeMap.put(99l,"15");

        subjectMap.put( 60l,"1");
        subjectMap.put( 80l,"2");
        subjectMap.put( 10l,"3");
        subjectMap.put( 50l,"4");
        subjectMap.put( 40l,"5");
        subjectMap.put( 20l,"6");
        subjectMap.put( 30l,"7");
        subjectMap.put( 70l,"8");
        subjectMap.put( 90l,"9");
        subjectMap.put( 999l,"0");

        levelMap.put(1,"1");
        levelMap.put(2,"2");
        levelMap.put(3,"3");
        levelMap.put(4,"4");
        levelMap.put(5,"5");
        levelMap.put(6,"6");
        levelMap.put(7,"7");
        levelMap.put(8,"8");


        teacherAccessTypeMap.put(0,"1");
        teacherAccessTypeMap.put(1,"4");
        teacherAccessTypeMap.put(2,"5");

        teacherJobStatusTypeMap.put(0,"0");
        teacherJobStatusTypeMap.put(1,"1");
        teacherJobStatusTypeMap.put(2,"1");



    }


    @Resource
    private IPeriodService periodService;

    @Resource
    private ITeacherService teacherService;


    @Resource
    private ICourseTemplateService courseTemplateService;

    @Resource
    private IStudentService studentService;

    @Resource
    private IStudentRegService studentRegService;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private ICampusService campusService;


    @Value("${tas.api.prefix}")
    private String apiPrefix ;

    @Value("${tas.api.key}")
    private String apiKey ;


    private LoadingCache<Long,Optional<Campus>> campusLoadingCache ;
    @PostConstruct
    public void init(){
        this.campusLoadingCache =  CacheUtils.buildCache(campusService::findById) ;
    }

    @Override
    public void pushCourseClass(Long areaTeamId, List<CourseClass> courseClasses) {

        if(!sysCode2AreaTeamId.containsKey(areaTeamId) || courseClasses == null || courseClasses.size() == 0 ){
            return;
        }

        List<Long> periodIds = Utils.unique( BeanUtils.toList(courseClasses,"periodId") );
        List<String> teacherIds =  BeanUtils.toList(courseClasses,"teacherIds") ;
        teacherIds=BeanUtils.removeNullAndEmpty(teacherIds);
        List<Long> campusIds = Utils.unique( BeanUtils.toList(courseClasses,"campusId") );
        List<Long> courseTemplateIds = Utils.unique( BeanUtils.toList(courseClasses,"courseTemplateId") );


        List<Period> periods = periodService.findByIds(periodIds);
        List<Teacher> teachers = teacherService.findByIds(Utils.unique(Utils.toLongIds(StringUtils.join(teacherIds,","))));
        List<Campus> campuses = campusService.findByIds(campusIds);
        List<CourseTemplate> courseTemplates = courseTemplateService.findByIds(courseTemplateIds);

        JSONArray lessons = toLesson(courseClasses,courseTemplates );
        JSONArray classes = toClass(courseClasses );
        JSONArray users = toUser(teachers );
        JSONArray trimester = toTrimester(periods) ;
        JSONArray school = toSchool(campuses);

        JSONObject requestBody = new JSONObject();
        JSONObject body = new JSONObject();

        body.put("classes",classes);
        body.put("lesson",lessons);
        body.put("school",school);
        body.put("user",users);
        body.put("trimester",trimester);
        requestBody.put("body",body);
        requestBody.put("head",getHead(sysCode2AreaTeamId.get(areaTeamId)));
        sendToTas(apiPrefix + "/out/defind/all/update" , apiKey,requestBody);

    }

    @Override
    public void pushCampus(Long areaTeamId, List<Campus> campuses) {
        if(!sysCode2AreaTeamId.containsKey(areaTeamId) || campuses == null || campuses.size() == 0 ){
            return;
        }

        JSONArray school = toSchool(campuses);

        JSONObject requestBody = new JSONObject();
        JSONObject body = new JSONObject();

        body.put("list",school);
        requestBody.put("body",body);
        requestBody.put("head",getHead(sysCode2AreaTeamId.get(areaTeamId)));
        sendToTas(apiPrefix + "/out/defind/school/update" , apiKey,requestBody);
    }

    @Override
    public void pushTeacher(Long areaTeamId, List<Teacher> teachers) {

        if(!sysCode2AreaTeamId.containsKey(areaTeamId) || teachers == null || teachers.size() == 0 ){
            return;
        }

        JSONArray users = toUser(teachers );

        JSONObject requestBody = new JSONObject();
        JSONObject body = new JSONObject();

        body.put("list",users);
        requestBody.put("body",body);
        requestBody.put("head",getHead(sysCode2AreaTeamId.get(areaTeamId)));
        sendToTas(apiPrefix + "/out/defind/user/update" , apiKey,requestBody);

    }

    private JSONObject getHead(String sysCode ){
        JSONObject header = new JSONObject();
        header.put("messageSn" , IdWorker.getId());
        header.put("reqTime" , Utils.formatDate(new Date(),"yyyyMMddHHmmss"));
        header.put("sysCode" , sysCode);
        header.put("version" , "100");
        return header;
    }

    private void sendToTas( String apiUrl , String apiKey , JSONObject requestBody) {

        String content = JSONObject.toJSONString(requestBody, SerializerFeature.WriteNonStringValueAsString);
        String signature = DigestUtils.md5Hex(content + apiKey );
        Request request = new Request.Builder()
                .url(apiUrl)
                .header("signature", signature)
                .post(RequestBody.create(MediaType.parse("application/json"), content))
                .build();

        String res = null ;
        try {
            Response response = httpClient.newCall(request).execute();
            res = response.body().string();
        } catch (IOException e) {
            throw new RuntimeException("Network error",e );
        }

        log.info("Tas Resp :" + res);

/*        JSONObject resObj = JSONObject.parseObject(res);

        if(!resObj.getJSONObject("head").getString("respCode").equals("1001")){
            throw new BusException("Tas handle fail \n " + resObj.getJSONObject("head").getString("respMess"));
        }*/

    }

    private JSONArray toSchool(List<Campus> campuses) {
        JSONArray list = new JSONArray();
        for(Campus campus : campuses ){
            JSONObject item = new JSONObject();
            item.put("oId",campus.getCampusId());
            item.put("name",campus.getCampusName());

            list.add(item);
        }

        return list;
    }

    private JSONArray toTrimester(List<Period> periods) {
        JSONArray list = new JSONArray();
        for(Period period : periods ){
            JSONObject item = new JSONObject();
            item.put("oId",period.getPeriodId());
            item.put("name",period.getPeriodName());

            list.add(item);
        }
        return list;
    }

    private JSONArray toUser(List<Teacher> teachers) {
        JSONArray list = new JSONArray();

        for(Teacher teacher : teachers ){
            JSONObject item = new JSONObject();
            item.put("oId",teacher.getTeacherId());
            item.put("account",teacher.getPhoneNo());
            item.put("username",teacher.getTeacherName());
            item.put("headImg",teacher.getAvatarUrl());
            item.put("phone",teacher.getPhoneNo());
            item.put("idCard",teacher.getIdNo());
            item.put("status",teacher.getStatus());
            item.put("sex",( teacher.getSex() + 2 ) % 3 );
            item.put("type", teacherAccessTypeMap.get( teacher.getAccessType() ));
            item.put("jobStatus", teacherJobStatusTypeMap.get( teacher.getJobNature() ));
            item.put("workStatus", ( teacher.getStatus() + 1 ) % 2 ) ;

            if(StringUtils.isEmpty(teacher.getTeachGradeIds())){
                teacher.setTeachGradeIds("11,12,13,14,15,16,21,22,23,31,32,33,99");
            }

            if(StringUtils.isEmpty(teacher.getTeachSubjectIds())){
                teacher.setTeachSubjectIds("10,20,30,40,50,60,70,80,90,999");
            }



            item.put("subjects" , Lists.transform(Arrays.asList(teacher.getTeachSubjectIds().split(",")),i -> subjectMap.get(Long.valueOf(i)) ) );
            item.put("sections", Lists.transform(Arrays.asList(teacher.getTeachGradeIds().split(",")),i -> gradeMap.get(Long.valueOf(i)) ));


            list.add(item);
        }
        return list;
    }

    private JSONArray toClass(List<CourseClass> courseClasses) {
        JSONArray list = new JSONArray();

        for(CourseClass courseClass : courseClasses ) {
            JSONObject item = new JSONObject();
            item.put("oId",courseClass.getClassId());
            item.put("name",courseClass.getClassName());
            item.put("num",courseClass.getClassNo());
            item.put("school",courseClass.getCampusId());
            item.put("status",courseClass.getStatus());
            item.put("progressStatus",courseClass.getProgressStatus());
            item.put("openTime",courseClass.getStartCourseTime());
            item.put("closeTime",courseClass.getEndCourseTime());
            item.put("lessonOId",courseClass.getCourseTemplateId());
            item.put("year",extractYear(courseClass.getPeriodName()).substring(2));
            item.put("trimester",courseClass.getPeriodId());
            item.put("userOIds", StringUtils.isNotEmpty(courseClass.getTeacherIds()) ? courseClass.getTeacherIds().split(",") : null );
            list.add(item);
        }

        return list;
    }

    private JSONArray toLesson(List<CourseClass> courseClasses,List<CourseTemplate> courseTemplates) {

        JSONArray list = new JSONArray();

        //Map<Long,CourseTemplate> courseTemplateId2CourseTemplate = BeanUtils.toMap(courseTemplates,"courseTemplateId");

        for(CourseTemplate courseTemplate : courseTemplates ){
            JSONObject item = new JSONObject();
            item.put("oId",courseTemplate.getCourseTemplateId() );
            item.put("name",courseTemplate.getCourseName());
            item.put("chapterNum",courseTemplate.getLectureAmount());
            item.put("remark",courseTemplate.getCourseDescription());
            item.put("level",levelMap.get(courseTemplate.getLevel()));
            item.put("subject",subjectMap.get(courseTemplate.getSubjectId()));
            item.put("section",gradeMap.get(courseTemplate.getGradeId()));
            item.put("year",extractYear(courseTemplate.getPeriodName()).substring(2));
            item.put("trimester",courseTemplate.getPeriodId());

            list.add(item);
        }
        return list;

    }


    private String extractYear(String periodName ) {
        Matcher matcher = yearPattern.matcher(periodName);
        if( matcher.find()){
            return matcher.group();
        }
        return "2018";
    }


    static class User {
        private Long userId ;

        private String name ;

        public User(Long userId, String name) {
            this.userId = userId;
            this.name = name;
        }

        public Long getUserId() {
            return userId;
        }

        public void setUserId(Long userId) {
            this.userId = userId;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

    public static void main(String[] args) {

        List<User> users  = Arrays.asList(new User(1l, "1"),new User(1l, "1"));

        ImmutableListMultimap<Long, User> index = Multimaps.index(users, u -> u.getUserId());

        System.out.println(index);


    }


    @Override
    public void pushStudent(List<Student> studentList) throws ExecutionException {
        JSONObject requestBody = new JSONObject();


        List<TasStudentVo> tasStudentVoList=new ArrayList<>(studentList.size());
        for (Student student:studentList){
            if (StringUtils.isEmpty(student.getName()) || StringUtils.isEmpty(student.getPhoneNo())){
                continue;
            }
            TasStudentVo tasStudentVo=new TasStudentVo();

            if (student.getLastRegCampusId()!=null){

                Optional<Campus> campusOptional=campusLoadingCache.get(student.getLastRegCampusId());

                Campus campus = campusOptional.isPresent()?campusLoadingCache.get(student.getLastRegCampusId()).get():null;
                if (campus!=null && campus.getAreaTeamId()!=null){
                    tasStudentVo.setAreaTeamId(campus.getAreaTeamId());
                    tasStudentVo.setoId(student.getStudentId());
                    tasStudentVo.setName(student.getName());
                    tasStudentVo.setSex(student.getSex());
                    if (StringUtils.isNotEmpty(gradeMap.get(student.getGradeId()))){
                        tasStudentVo.setSection(Integer.parseInt(gradeMap.get(student.getGradeId())));
                    }
                    tasStudentVo.setPhoneNo(student.getPhoneNo());
                    tasStudentVo.setStudentNo(student.getStudentNo());
                    tasStudentVoList.add(tasStudentVo);
                }
            }
        }
        Map<Long, List<TasStudentVo>> areaTeamTasStudentMap = BeanUtils.toGroup(tasStudentVoList, "areaTeamId");
        for (Map.Entry<Long,List<TasStudentVo>> entry:areaTeamTasStudentMap.entrySet()){
            if (sysCode2AreaTeamId.get(entry.getKey())==null){
                continue;
            }
            List<TasStudentVo> studentVos=entry.getValue();
            JSONObject body = new JSONObject();
            body.put("list",studentVos);
            requestBody.put("body",body);
            requestBody.put("head",getHead(sysCode2AreaTeamId.get(entry.getKey())));
            sendToTas(apiPrefix + "/out/defind/student/update" , apiKey,requestBody);
        }

    }


    @Override
    public void pushStudentReg(List<StudentReg> studentRegList) {

        List<Long> studentIds = BeanUtils.toList(studentRegList, "studentId");
        List<Long> classIds = BeanUtils.toList(studentRegList, "classId");
        List<StudentClassVo> studentClassVoList=studentRegService.findStudentClassList(studentIds,classIds);
        List<TasRegVo> tasRegVoList=new ArrayList<>(studentRegList.size());
        List<StudentClassVo> recordList=new ArrayList<>(studentRegList.size());
        for (StudentClassVo studentClassVo:studentClassVoList){
            if (sysCode2AreaTeamId.get(studentClassVo.getAreaTeamId())==null){
                continue;
            }

            String recordStudentRegMaxUpdateTime=(String) redisTemplate.opsForHash().get("studentClassMaxUpdateTime",studentClassVo.getStudentId()+"_"+studentClassVo.getClassId());
            if (StringUtils.isNotEmpty(recordStudentRegMaxUpdateTime) && studentClassVo.getUpdateTime().getTime()<Long.parseLong(recordStudentRegMaxUpdateTime)){
                continue;
            }
            TasRegVo tasRegVo=new TasRegVo();
            BeanUtils.copyProperties(tasRegVo,studentClassVo);
            tasRegVoList.add(tasRegVo);
            recordList.add(studentClassVo);
        }
        if (tasRegVoList.size()>0){
            Map<Long, List<TasRegVo>> areaTeamRegMap = BeanUtils.toGroup(tasRegVoList, "areaTeamId");
            for (Map.Entry<Long,List<TasRegVo>> entry:areaTeamRegMap.entrySet()){
                JSONObject requestBody = new JSONObject();
                JSONObject body = new JSONObject();

                body.put("list",entry.getValue());
                requestBody.put("body",body);
                requestBody.put("head",getHead(sysCode2AreaTeamId.get(entry.getKey())));
                sendToTas(apiPrefix + "/out/defind/student/stuClass" , apiKey,requestBody);
            }

        }

        for (StudentClassVo studentClassVo:recordList){

            redisTemplate.opsForHash().put("studentClassMaxUpdateTime",studentClassVo.getStudentId()+"_"+studentClassVo.getClassId(),studentClassVo.getUpdateTime().getTime()+"");
        }
    }

}
